//==========================================================================
// Copyright (c) 2000-2008,  Elastos, Inc.  All Rights Reserved.
//==========================================================================
#include <ddk.h>

#include <queue.h>

void EnQueueProc(int eType, int nCode, void *pParam);

class EventQueue : public Queue {
public:
    int     m_EventType;
    DzEvent   m_Wait;
    EventQueue(int eType, int Size=4, int Len=32) : Queue(Size, Len)
    {
        m_EventType = eType;
//        AddNotify(eType, (void*)EnQueueProc);
    };
};

class EventQueues : public CDeviceDriver {
public:
    EventQueues(int MaxItem);

    ~EventQueues();

    CARAPI Read(
        /* [in] */ Int64 u64Offset,
        /* [in] */ Int32 bytesToRead,
        /* [out] */ MemoryBuf * pBuffer,
        /* [out] */ IEvent * * ppCompletionEvent);

    CARAPI Write(
        /* [in] */ Int64 u64Offset,
        /* [in] */ const MemoryBuf &buffer,
        /* [out] */ Int32 * pBytesWritten,
        /* [out] */ IEvent * * ppCompletionEvent);

    CARAPI Control(
        /* [in] */ Handle32 nControlCode,
        /* [in] */ const MemoryBuf &inBuffer,
        /* [out] */ MemoryBuf * pOutBuffer,
        /* [out] */ IEvent * * ppCompletionEvent);

    // It's Maybe have no use for the keyboard driver.
    virtual void Dispose() {}

public:
    const int MAX_ITEMS;// The actual items
    EventQueue* *Queues; // array for all queues
};

static EventQueues *g_EventQueues = NULL;

IDeviceDriver *g_pEventQueues = NULL;

void EnQueueProc(int eType, int nCode, void *pParam)
{
    //printf("[%d]: nCode(%d) %x\n", eType, nCode, pParam);
    EventQueue *Q = g_EventQueues->Queues[eType];
    if (Q && pParam) {
        Q->Enqueue(pParam);
        Q->m_Wait.NotifyByIsr();
    }
    return;
}

EventQueues::EventQueues(int MaxItem) : MAX_ITEMS( MaxItem )
{
    Queues = new EventQueue* [MAX_ITEMS];
    assert(Queues);
    memset(Queues, 0, sizeof(EventQueue*)*MAX_ITEMS);//Clean all queue
    return ;
}

EventQueues::~EventQueues()
{
    for(int i = 0; i < MAX_ITEMS; i++) {
        if (NULL != g_EventQueues->Queues[i]) {
            delete g_EventQueues->Queues[i];
        }
    }
    delete[] Queues;
}

ECode EventQueues::Read(
        /* [in] */ Int64 u64Offset,//bASync:EventType
        /* [in] */ Int32 bytesToRead,
        /* [out] */ MemoryBuf * pBuffer,
        /* [out] */ IEvent * * ppCompletionEvent)
{
    int EventType = (int)u64Offset;
    bool bASync = (EventType & 0x80000000)?true:false;

    EventType &= 0xffff;
    assert(EventType >= 0 && EventType < EVENT_MAX);

    EventQueue *Q = g_EventQueues->Queues[EventType];
    if (NULL == Q) {
        return E_ACCESS_DENIED;
    }
    if (pBuffer == NULL) {
        return E_INVALID_ARGUMENT;
    }
    assert(pBuffer->GetCapacity() >= Q->ELEM_SIZ);
    int ret = -1;
    WaitResult wr;
    while (0 != ret) {
        ret = Q->Dequeue((char*)pBuffer->GetPayload());
        if (0 == ret) {
            pBuffer->SetUsed(Q->ELEM_SIZ);
            break;
        }
        else if (bASync) {//no data
            return E_FAIL;//E_NOT_READY
        }
        Q->m_Wait.Wait(&wr, NULL);
    }

    return NOERROR;
}

ECode EventQueues::Write(
        /* [in] */ Int64 u64Offset,
        /* [in] */ const MemoryBuf &buffer,
        /* [out] */ Int32 * pBytesWritten,
        /* [out] */ IEvent * * ppCompletionEvent)
{
    return E_NOT_IMPLEMENTED;
}

ECode EventQueues::Control(
        /* [in] */ Handle32 nControlCode,
        /* [in] */ const MemoryBuf &inBuffer,
        /* [out] */ MemoryBuf * pOutBuffer,
        /* [out] */ IEvent * * ppCompletionEvent)
{
    ECode ec = E_ACCESS_DENIED;
    int nFuncCode = nControlCode & 0xffff;
    switch(nFuncCode) {
        case 1: {//Create New Queue
            if (inBuffer.GetUsed() >= (int)sizeof(QueueInfo)) {
                QueueInfo *pInfo = (QueueInfo *)inBuffer.GetPayload();
                int e = pInfo->eType;
                if (NULL == g_EventQueues->Queues[e]) {
                    EventQueue *q = new EventQueue(e, pInfo->Size, pInfo->Len);
                    assert(q);
                    g_EventQueues->Queues[pInfo->eType] = q;
                    ec = NOERROR;
                }
            }
            break;
        }
        case 2: {//Reserve: Free queue
            int EventType = nControlCode >> 16;
            assert(EventType < 128);
            EventQueue *Q = g_EventQueues->Queues[EventType];
            if (NULL == Q) break;

            //g_EventQueues->Queues[EventType] = NULL;
            //_SysUnhook(EventType);
            AddNotify(EventType, NULL);//RemoveNotify
            //cprintf("Clean queue %d hook\n", EventType);
            ec = NOERROR;
            break;
        }
        case 5: {//lap over queues
            int EventType = nControlCode >> 16;
            assert(EventType < 128);
            EventQueue *Q = g_EventQueues->Queues[EventType];
            if (NULL == Q) break;

            int n = *(int *)inBuffer.GetPayload();
            assert(n > 0 && n < 128);
            //if (NULL != g_EventQueues->Queues[n]) return E_ACCESS_DENIED;

            g_EventQueues->Queues[n] = Q;
            AddNotify(n, (void*)EnQueueProc);
            ec = NOERROR;
            //cprintf("lap over queue from %d to %d\n", EventType, n);
            break;
        }
        case 10: {//Drain special Queue data
            int EventType = nControlCode >> 16;
            assert(EventType < 128);
            EventQueue *Q = g_EventQueues->Queues[EventType];

            if (NULL == Q) break;

            if (pOutBuffer == NULL) {
                return E_INVALID_ARGUMENT;
            }
            assert(pOutBuffer->GetCapacity() >= Q->ELEM_SIZ);

            //
            int ret, Cnt = 0;
            do {
                ret = Q->Dequeue((char*)pOutBuffer->GetPayload());
                if (0 != ret) break;
                Cnt ++;
            }while (1);

            pOutBuffer->SetUsed(Q->ELEM_SIZ * Cnt);
            ec = NOERROR;
            break;
        }
        default:
            break;
    }
    return ec;
}

EXTERN IDeviceDriver * CDECL CreateEventQueues(uint_t uDeviceNo, void *pvParameter)
{
    g_EventQueues = new EventQueues(EVENT_MAX);
    g_pEventQueues =  (IDeviceDriver *)g_EventQueues;
    assert(g_pEventQueues);

    EventQueue *kq = new EventQueue(EVENT_KEYPAD, 8/*sizeof(KPD_DATA)*/, 64);
    AddNotify(EVENT_KEYPAD, (void*)EnQueueProc);
    g_EventQueues->Queues[EVENT_KEYPAD] = kq;

    EventQueue *tq = new EventQueue(EVENT_TOUCH, 16/*sizeof(TOUCH_DATA)*/, 32);
    AddNotify(EVENT_TOUCH, (void*)EnQueueProc);
    g_EventQueues->Queues[EVENT_TOUCH] = tq;


    EventQueue *cq = new EventQueue(EVENT_COMMON, 20/*EventNum+Parameters*/, 8);
    AddNotify(EVENT_COMMON, (void*)EnQueueProc);
    g_EventQueues->Queues[EVENT_COMMON] = cq;

    return (IDeviceDriver *)g_EventQueues;
}
